package zpe.controledeacesso.service;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import zpe.controledeacesso.model.bean.Email;
import zpe.controledeacesso.model.bean.GrupoDeUsuario;
import zpe.controledeacesso.model.bean.Localidade;
import zpe.controledeacesso.model.bean.Usuario;
import zpe.controledeacesso.security.Acesso;
import zpe.controledeacesso.util.EmailUtil;
import zpe.controledeacesso.util.StringUtils;
import zpe.genericcrud.exception.ApplicationException;
import zpe.genericcrud.service.GenericService;

@Service
public class UsuarioService extends GenericService<Usuario> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1143979130133013845L;
	
	@Autowired PermissoesService servicePermissoes;
	@Autowired UnidadeTrabalhoService serviceUnidadeDeTrabalho;
	@Autowired GrupoDeUsuariosService serviceGrupoDeUsuarios;
	
	@Override
	@Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class )
	public Usuario insert(Usuario usuario ) {
		
		usuario.setLogin( usuario.getLogin().trim() );
		usuario.setEmail( usuario.getEmail().trim() );
		validaLogin( usuario, false );
		validaEmail( usuario, false);
		validaCpf( usuario, false);
		
		usuario.setSenha( StringUtils.md5( usuario.getSenha() ) );
		usuario.setDataCriacao( new Date() );
		
		super.insert(usuario);
		
		// fim do envio
		return usuario;
	}
	
	public void enviarEmail(String enderecoEmail, String login, String senha) {
		
		Email email = new Email();

		email.setServidor( "smtp.seduc.ce.gov.br" );
		email.setUsuario( "sistemarh@seduc.ce.gov.br" );
		email.setSenha( "sistemarh" );
		email.setDestino( enderecoEmail );
		email.setRemetente( "SEDUC-CE  <sistemarh@seduc.ce.gov.br>" );
		email.setAssunto( "SEDUC-CE - Cadastro no Controle de Acesso" );
		email.setMensagem( Email.geraMensagem( login, senha ) );
		
		//envia email
		EmailUtil emailUtil = new EmailUtil();
		emailUtil.enviarEmail( email );
	}

	@Transactional( propagation = Propagation.REQUIRED, rollbackFor = Exception.class )
	public void atualizarSenhaToLogin(Usuario usuario) {
		
		usuario.setSenha(StringUtils.md5( usuario.getLogin() ));
		getDao().getEntityManager().bulkUpdate("update Usuario set senha = ? where id = ?", usuario.getSenha(), usuario.getId());
	}
	
	@Transactional( propagation = Propagation.REQUIRED, rollbackFor = {RuntimeException.class, Exception.class} )
	public void reiniciarSenha( Usuario usuario ){
		usuario.setLogin( usuario.getLogin() != null ? usuario.getLogin().trim() : null );
		usuario.setEmail( usuario.getEmail() != null ? usuario.getEmail().trim() : null );
		List<Usuario> l = findByFilter( Usuario.class, usuario.notEmptyFields() );
		
		if(l.isEmpty()){
			throw new ApplicationException( "Os dados informados não conferem!" );
		} 
		
		usuario = l.get(0);
		String chave = EmailUtil.geraSenha();
		usuario.setAtualizouSenha("N");
		usuario.setSenha( StringUtils.md5( chave ) );
		
		super.update( usuario );

		// envio do e-mail
		Email email = new Email();
		//email.setServidor( "172.31.4.26" );
		email.setServidor( "smtp.seduc.ce.gov.br" );
		email.setUsuario( "sistemarh@seduc.ce.gov.br" );
		email.setSenha( "sistemarh" );
		email.setDestino( usuario.getEmail() );
		email.setRemetente( "SEDUC-CE  <sistemarh@seduc.ce.gov.br>" );
		email.setAssunto( "SEDUC-CE - Senha de Acesso" );
		email.setMensagem( Email.geraMensagemRedefinirSenha( usuario.getLogin(), chave ) );
		
		EmailUtil emailUtil = new EmailUtil();
		emailUtil.enviarEmail( email );
	}
	
	@SuppressWarnings("unchecked")
	public Usuario findByLogin(String login) {
		DetachedCriteria c = DetachedCriteria.forClass(Usuario.class);
		c.add(Restrictions.eq("login", login.trim()).ignoreCase());
		List<Usuario> l = getDao().getEntityManager().findByCriteria(c);
		if (!l.isEmpty()) {
			return l.get(0);
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public Boolean findByEmail(String email) {
		DetachedCriteria c = DetachedCriteria.forClass(Usuario.class);
		c.add(Restrictions.eq("email", email).ignoreCase());
		List<Usuario> l = getDao().getEntityManager().findByCriteria(c);
		if (!l.isEmpty()) {
			return true;
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	public void validaLogin(Usuario usuario, boolean isUpdating) {
		DetachedCriteria c = DetachedCriteria.forClass(Usuario.class);
		c.add(Restrictions.eq("login", usuario.getLogin()).ignoreCase());
		if (isUpdating) {
			List<Usuario> l = getDao().getEntityManager().findByCriteria(c);
			if (!l.isEmpty()) {
				boolean pertenceAOutro = true;
				for( Usuario u : l ){
					if( u.getId().equals( usuario.getId() ) ){
						pertenceAOutro = false;
						break;
					}
				}
				if( pertenceAOutro ){
					throw new ApplicationException("O login " + usuario.getLogin()
							+ " já encontra-se em uso.");
				}
			}
		}else{
			List<Usuario> l = getDao().getEntityManager().findByCriteria(c);
			if (!l.isEmpty()) {
				throw new ApplicationException("O login " + usuario.getLogin()
					+ " já encontra-se em uso.");
			}
		}
	}

	/**
	 * 
	 * @param usuario
	 * @param isUpdating
	 */
	@SuppressWarnings({ "unchecked", "unused" })
	public void validaCpf(Usuario usuario, boolean isUpdating) {
		DetachedCriteria c = DetachedCriteria.forClass(Usuario.class);

		if (isUpdating) {
			List<Usuario> l = getDao().getEntityManager().findByCriteria(c);
			if (!l.isEmpty()) {
				boolean pertenceAOutro = true;
				for( Usuario u : l ){
					if( u.getId().equals( usuario.getId() ) ){
						pertenceAOutro = false;
						break;
					}
				}
			}
		} 
	}

	@SuppressWarnings("unchecked")
	public void validaEmail( Usuario usuario, boolean isUpdating ) { 
		DetachedCriteria c = DetachedCriteria.forClass(Usuario.class);
		c.add(Restrictions.eq("email", usuario.getEmail()).ignoreCase());

		if (isUpdating) {
			List<Usuario> l = getDao().getEntityManager().findByCriteria(c);
			if (!l.isEmpty()) {
				boolean pertenceAOutro = true;
				for( Usuario u : l ){
					if( u.getId().equals( usuario.getId() ) ){
						pertenceAOutro = false;
						break;
					}
				}
				if( pertenceAOutro ){
					throw new ApplicationException("Email "
						+ usuario.getEmail() + " já cadastrado.");
				}
			}
		} else {
			List<Usuario> l = getDao().getEntityManager().findByCriteria(c);
			if (!l.isEmpty()) {
				throw new ApplicationException("Email "
						+ usuario.getEmail() + " já cadastrado.");
			}
		}

	}

	@Transactional( propagation = Propagation.REQUIRED, rollbackFor = {RuntimeException.class, Exception.class} )
	public void removeAll(Collection<Usuario> collection, Usuario usuarioLogado) {
		int modulo = Acesso.getModuloSistema();
		List<GrupoDeUsuario> grupos = getServiceGrupoDeUsuarios().findByNivel( usuarioLogado.getMaiorNivel(modulo), modulo);
		List<GrupoDeUsuario> gruposRemove = new ArrayList<GrupoDeUsuario>();
		for( Usuario u : collection ){
			for( GrupoDeUsuario g : grupos ){
				if( u.getGrupos().contains( g ) ){
					gruposRemove.add( g );
				}
			}
			if( !gruposRemove.isEmpty() ){
				u.getGrupos().removeAll( gruposRemove );
				super.update( u );
			}
		}		
	}
	
	@SuppressWarnings("unchecked")
	public List<Usuario> findByGrupo( GrupoDeUsuario grupo ){
		DetachedCriteria c  = DetachedCriteria.forClass( Usuario.class )
			.createAlias( "grupos", "g")
			.add( Restrictions.eq( "g.id", grupo.getId() ) )
			.addOrder( Order.asc( "descricao" ) );
		
		return getDao().getEntityManager().findByCriteria( c );
	}
	
	@SuppressWarnings("unchecked")
	public List<Usuario> findByGrupo( GrupoDeUsuario grupo, Localidade loc ){
		DetachedCriteria c  = DetachedCriteria.forClass( Usuario.class )
			.createAlias( "grupos", "g")
			.createAlias( "unidadeDeTrabalho.localidade", "l")
			.add( Restrictions.eq( "g.id", grupo.getId() ) )
			.add( Restrictions.eq( "l.id", loc.getId() ) )
			.addOrder( Order.asc( "descricao" ) );
		
		return getDao().getEntityManager().findByCriteria( c );
	}
	
	/**
	 * @return the servicePermissoes
	 */
	public PermissoesService getServicePermissoes() {
		return servicePermissoes;
	}

	/**
	 * @param servicePermissoes
	 *            the servicePermissoes to set
	 */
	public void setServicePermissoes(PermissoesService servicePermissoes) {
		this.servicePermissoes = servicePermissoes;
	}

	public UnidadeTrabalhoService getServiceUnidadeDeTrabalho() {
		return serviceUnidadeDeTrabalho;
	}

	public void setServiceUnidadeDeTrabalho(
			UnidadeTrabalhoService serviceUnidadeDeTrabalho) {
		this.serviceUnidadeDeTrabalho = serviceUnidadeDeTrabalho;
	}

	public GrupoDeUsuariosService getServiceGrupoDeUsuarios() {
		return serviceGrupoDeUsuarios;
	}

	public void setServiceGrupoDeUsuarios(
			GrupoDeUsuariosService serviceGrupoDeUsuarios) {
		this.serviceGrupoDeUsuarios = serviceGrupoDeUsuarios;
	}

}
